package util;

import java.util.BitSet;

/**
 * MyBitSet est une implémentation de bit set avec quelques fonctionnalités
 * supplémentaires adaptées au projet. mybitSet.get(0) est le bit de poids fort
 * et myBitSet(size - 1) est le bit de poids faible;
 * 
 */
public class MyBitSet extends BitSet implements Cloneable {

	private static final long serialVersionUID = -5403724265672221468L;
	int size;

	/**
	 * prend une string décrivant le Bit Set en caractères
	 * 
	 * @param sequence
	 *            la séquence des caractères
	 */
	public MyBitSet(String sequence) {
		sequence = sequence.replaceAll("\\s", "");
		this.size = sequence.length();
		for (int i = 0; i < sequence.length(); i++) {
			switch (sequence.charAt(i)) {
			case '1':
				set(i, true);
				break;
			case '0':
				set(i, false);
				break;
			default:
				throw new StringParseException();
			}
		}
	}

	public MyBitSet(int size) {
		super(size);
		this.size = size;
	}

	private MyBitSet(BitSet bs, int size) {
		this(size);
		for (int i = 0; i < size; i++) {
			this.set(i, bs.get(i));
		}
	}

	/**
	 * cette fonction réalise la permutation d'un BitSet par le vecteur de
	 * permutation. Si permuteArray est { 3, 4, 1, 6, 2, 5 } le premier bit de
	 * bs devient alors sont 3ème bit le 2ème son 4ème etc. Le tableau des
	 * permutations sont remplis par des valeurs comprises dans [1 ,
	 * permuteArray.length]
	 * 
	 * @param bs
	 *            le bitSet à permuter
	 * @param permuteArray
	 *            le vecteur de permutation.
	 */
	public MyBitSet permute(int[] permuteArray) {
		MyBitSet buf = new MyBitSet(permuteArray.length);
		for (int i = 0; i < permuteArray.length; i++) {
			buf.set(i, this.get(permuteArray[i] - 1));
		}
		return buf;
	}

	/**
	 * apply a shift on the bit set.
	 * 
	 * @param nombreShift
	 *            numberShift > 0 => Right shift numberShift < 0 => left shift
	 */
	public void setShift(int numberShift) {
		while (numberShift < 0) {
			numberShift += size;
		}
		MyBitSet buf = this.clone();
		for (int i = 0; i < size; i++) {
			this.set((i + numberShift) % size, buf.get(i));
		}
	}

	/**
	 * différent de size() défini par BitSet ici size retourne la taille fictive
	 * du BitSet. c'est cette taille qui importe pour le projet DES
	 * 
	 * @return
	 */
	public int getSize() {
		return size;
	}

	/**
	 * différent de size() défini par BitSet ici size retourne la taille fictive
	 * du BitSet. c'est cette taille qui importe pour le projet DES
	 * 
	 * @return
	 */
	public void setSize(int size) {
		this.size = size;
	}

	/**
	 * Split le bit set et retourne un tableau de 2 MyBitSet indice 1 la partie
	 * droite indice 0 la partie gauche
	 * 
	 * @return
	 */
	public MyBitSet[] split() {
		if (getSize() % 2 != 0) {
			throw new CantSplitUnpairBitSetException();
		}
		MyBitSet[] result = new MyBitSet[2];
		result[0] = this.get(0, getSize() / 2);
		result[1] = this.get(getSize() / 2, getSize());
		return result;
	}

	/**
	 * 
	 */
	public MyBitSet get(int toIndex, int fromIndex) {
		if (fromIndex < toIndex) {
			throw new ArgumentException();
		}
		return new MyBitSet(super.get(toIndex, fromIndex), fromIndex - toIndex);
	}

	/**
	 * Concatène les deux ensembles de bit en un seul BitSet this = 110011
	 * 
	 * @param leftBitSet
	 *            001100
	 * @return le BitSet concaténé dans l'exemple 001100 110011
	 */
	public MyBitSet concat(MyBitSet rightBitSet) {
		MyBitSet result = new MyBitSet(this.getSize() + rightBitSet.getSize());
		for (int i = 0; i < this.getSize(); i++) {
			result.set(i, this.get(i));
		}
		for (int i = 0; i < rightBitSet.getSize(); i++) {
			result.set(rightBitSet.getSize() + i, rightBitSet.get(i));
		}
		return result;
	}

	/**
	 * clone l'élément MyBitSet
	 */
	public MyBitSet clone() {
		return new MyBitSet(this, this.getSize());
	}

	/**
	 * 
	 * @param bs
	 * @return vrai si la séquence est la même et si la taille est la même.
	 */
	public boolean equals(MyBitSet bs) {
		return (this.size == bs.size && super.equals(bs));
	}

	public static void main(String[] args) {
		MyBitSet bs = new MyBitSet("1001 0110");
		System.out.println(bs.print());
		MyBitSet[] bss = bs.split();
		System.out.println(bss[0].print());
		System.out.println(bss[1].print());
		bs = bss[0].concat(bss[1]);
		System.out.println(bs.print());

		int[] p = { 8, 2, 3, 4, 5, 6, 7 };
		bs = bs.permute(p);
		System.out.println(bs.print());

		bs = new MyBitSet("111101");
		bs.setShift(-1000);
		System.out.println(bs.print());

	}

	public String print() {
		String s = "";
		for (int i = 0; i < size; i++) {
			s += (get(i)) ? '1' : '0';
		}
		return s;
	}
}
